/*
 * Copyright (c) 2011-2021, baomidou (jobob@qq.com).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cc.siyecao.mapper.wrapper;

import cc.siyecao.mapper.Criteria;
import cc.siyecao.mapper.conditions.Nested;
import cc.siyecao.mapper.conditions.Query;
import cc.siyecao.mapper.conditions.Update;

import java.util.List;
import java.util.function.Consumer;
import java.util.regex.Matcher;

import static cc.siyecao.mapper.enums.SqlKeyword.EQ;
import static cc.siyecao.mapper.provider.entity.EntityTable.DELIMITER;

/**
 * 查询条件封装
 */
@SuppressWarnings({"unchecked"})
public abstract class AbstractLambdaWrapper<R, W extends AbstractLambdaWrapper<R, W, N, F, T>, N extends AbstractCompareWrapper<N, R, T>, F extends AbstractFuncWrapper<F, R, T>, T> extends AbstractWrapper<W, R, T>
        implements Nested<N, W>, Update<W, R>, Query<W, R, F> {

    @Override
    public W and(boolean condition, Consumer<N> consumer) {
        return addNestedCondition( condition, "and", consumer );
    }

    @Override
    public W or(boolean condition, Consumer<N> consumer) {
        return addNestedCondition( condition, "or", consumer );
    }

    @Override
    public W nested(boolean condition, Consumer<N> consumer) {
        return addNestedCondition( condition, "nested", consumer );
    }

    @Override
    public W not(boolean condition, Consumer<N> consumer) {
        return addNestedCondition( condition, "not", consumer );
    }

    @Override
    public W select(R... columns) {
        for (R column : columns) {
            String columnName = columnName( column );
            String fieldName = fieldName( column );
            //fix 如果有设置 autoResultMap 就不能有 AS
            if (columnName.equals( fieldName )) {
                sqlSelect.addString( columnName );
            } else {
                Matcher matcher = DELIMITER.matcher( columnName );
                //eg: mysql `order` == field order | sqlserver [order] == field order
                if (matcher.find() && fieldName.equals( matcher.group( 1 ) )) {
                    sqlSelect.addString( columnName );
                } else {
                    sqlSelect.addString( columnName + " AS " + fieldName );
                }
            }
        }
        return typedThis;
    }

    @Override
    public W distinct() {
        distinct = true;
        return typedThis;
    }

    @Override
    public W select(Consumer<F> consumer) {
        final F instance = funcInstance();
        consumer.accept( instance );
        sqlSelect.addString( instance.getSqlSelect() );
        instance.clear();
        return typedThis;
    }

    @Override
    public W set(boolean condition, R column, Object val) {
        return maybeDo( condition, () -> setCriteria.addCriterion( columnName( column ), EQ, val ) );
    }

    /**
     * 多重嵌套查询条件
     *
     * @param condition 查询条件值
     */
    protected W addNestedCondition(boolean condition, String type, Consumer<N> consumer) {
        return maybeDo( condition, () -> {
            final N instance = nestedInstance( type );
            consumer.accept( instance );
            List<Criteria> criteriaList = instance.getCriterias();
            criterias.addAll( criteriaList );
            instance.clear();
        } );
    }

    /**
     * 子类返回一个自己的新对象
     */
    protected abstract N nestedInstance(String currentType);

    /**
     * 子类返回一个自己的新对象
     */
    protected abstract F funcInstance();

    @Override
    public Criteria getSetCriteria() {
        return setCriteria;
    }

}
